package com.erpoa.interceptor;

/**
 * @author chenjimin
 * @Title: LockMethodInterceptor
 * @ProjectName oaerp
 * @Description: 环绕增强, 实体类注意重写hash
 * @date 2020/9/417:27
 */

import com.erpoa.annotation.NoRepeatSubmit;
import com.erpoa.utils.PDAException;
import com.erpoa.utils.StringUtil;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Configuration;

import java.lang.reflect.Method;
import java.util.concurrent.TimeUnit;

@Aspect
@Configuration
public class LockMethodInterceptor {
    private static final Logger logger = LoggerFactory.getLogger(LockMethodInterceptor.class);

    private static final Cache<String, Object> CACHES = CacheBuilder.newBuilder()
            // 最大缓存 1000 个
            .maximumSize(1045)
            // 设置写缓存后 5 秒钟过期
            .expireAfterWrite(1, TimeUnit.SECONDS)
            .build();

    @Around("execution(public * *(..)) && @annotation(com.erpoa.annotation.NoRepeatSubmit)")
    public Object interceptor(ProceedingJoinPoint pjp) {
        MethodSignature signature = (MethodSignature) pjp.getSignature();
        Method method = signature.getMethod();
        NoRepeatSubmit localLock = method.getAnnotation(NoRepeatSubmit.class);
        String key = getKey(localLock.key(), pjp.getArgs());
        if (!StringUtil.isEmpty(key)) {
            if (CACHES.getIfPresent(key) != null) {
                logger.debug("有数据正在处理中，请再试一次！" + key);
                System.out.println("有数据正在处理中，请再试一次！" + key);
                throw new PDAException("有数据正在处理中，请再试一次！");
            }
            // 如果是第一次请求,就将 key 当前对象压入缓存中
            CACHES.put(key, key);
        }
        try {
            return pjp.proceed();
        } catch (Throwable throwable) {
            CACHES.invalidate(key);
            throw new PDAException("验证重复数据出错，请稍后重试！");

        } finally {
            // CACHES.invalidate(key);
        }
    }

    /**
     * key 的生成策略,如果想灵活可以写成接口与实现类的方式
     *
     * @param keyExpress 表达式
     * @param args       参数
     * @return 生成的key
     */
    private String getKey(String keyExpress, Object[] args) {
        for (int i = 0; i < args.length; i++) {

            keyExpress = keyExpress.replace("arg[" + i + "]", args[i].toString());
        }
        return keyExpress;
    }

}
